/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package Services;

import Beans.InternalNetworkBean;
import Beans.Requests.AuthorizationRequest;
import Beans.Requests.NetworkManagement.Components.DHCPServerConfiguration;
import Beans.Requests.NetworkManagement.CreateDHCPServerRequest;
import Beans.Requests.NetworkManagement.CreateHostOnlyInterfaceRequest;
import Beans.Requests.NetworkManagement.CreateInternalNetworkRequest;
import Beans.Requests.NetworkManagement.ModifyDHCPServerRequest;
import Beans.Requests.NetworkManagement.ModifyHostInterfaceRequest;
import Beans.Requests.NetworkManagement.ModifyInternalNetworkRequest;
import Beans.Requests.NetworkManagement.NetworkManagementRequest;
import Beans.Requests.NetworkManagement.RemoveDHCPServerRequest;
import Beans.Requests.NetworkManagement.RemoveHostOnlyInterfaceRequest;
import Beans.Requests.NetworkManagement.RemoveInternalNetworkRequest;
import Beans.Requests.RequestBean;
import Beans.Requests.SimpleAuthorizationRequest;
import Beans.Responses.NetworkManagementResponse;
import Beans.VirtualResourceBean;
import Managers.DatabaseManager;
import Managers.LogsManager;
import Managers.PermissionsManager;
import Managers.SessionsManager;
import Managers.Utilities.DatabaseManipulationResult;
import Managers.Utilities.RequestResponseWrapper;
import Managers.Utilities.ResponseMessagesContainer;
import Managers.VBoxConnectionsManager;
import Utilities.Constants;
import Utilities.Constants.VirtualResourceType;
import Utilities.ApplicationException;
import Utilities.ExceptionMessages;
import Utilities.Constants.NetworkType;
import Utilities.Functions;
import Utilities.ParameterNames;
import Utilities.PermissionsConstants;
import com.sun.xml.ws.commons.virtualbox_3_2.IDHCPServer;
import com.sun.xml.ws.commons.virtualbox_3_2.IHost;
import com.sun.xml.ws.commons.virtualbox_3_2.IHostNetworkInterface;
import com.sun.xml.ws.commons.virtualbox_3_2.IProgress;
import com.sun.xml.ws.commons.virtualbox_3_2.IVirtualBox;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.Holder;
import javax.xml.ws.WebServiceException;

/**
 * This servlet represents the application's Networks Management Service. <br><br>
 *
 * This service takes care of the management of DHCP servers, Host network
 * interfaces and internal networks (creation, removal, modification).
 *
 * @author Angel Sanadinov
 */
public class NetworksManagementService extends HttpServlet
{
    private DatabaseManager databaseManager;             //database manager
    private LogsManager logsManager;                     //logs manager
    private PermissionsManager permissionsManager;       //permissions manager
    private VBoxConnectionsManager connectionsManager ;  //virtualbox connections manager
    private SessionsManager sessionsManager;             //sessions manager
    private int maxOperationWaitTime; //max time in ms to wait for VirtualBox operation completion

    /**
     * Initializes the service and retrieves all necessary managers.
     *
     * @throws ServletException if an exception occurs that interrupts the servlet's
     *                          normal operation
     *
     * @see HttpServlet#init()
     */
    @Override
    public void init() throws ServletException
    {
        ServletContext context = getServletContext();
        databaseManager = (DatabaseManager)context.getAttribute(Constants.CONTEXT_DATABASE_MANAGER);
        logsManager = (LogsManager)context.getAttribute(Constants.CONTEXT_LOGS_MANAGER);
        permissionsManager = (PermissionsManager)context.getAttribute(Constants.CONTEXT_PERMISSIONS_MANAGER);
        connectionsManager = (VBoxConnectionsManager)context.getAttribute(Constants.CONTEXT_VBOX_CONNECTIONS_MANAGER);
        sessionsManager = (SessionsManager)context.getAttribute(Constants.CONTEXT_SESSIONS_MANAGER);

        maxOperationWaitTime = 0;
        try
        {
            maxOperationWaitTime =
                    Integer.parseInt(context.getInitParameter(ParameterNames.MAX_OPERATION_WAIT_TIME));
        }
        catch(NumberFormatException e)
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_WARNING,
                                       "Networks Management Service: Failed to parse initialization parameter"
                                       + " Default value (" + maxOperationWaitTime
                                       + ") for maxOperationWaitTime will be used.");
        }
    }

    /**
     * Shuts down the service and cleans up any object references.
     *
     * @see HttpServlet#destroy()
     */
    @Override
    public void destroy()
    {
        databaseManager = null;
        logsManager = null;
        permissionsManager = null;
        connectionsManager = null;
        sessionsManager = null;
    }

    /**
     * Processes a <code>GET</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the GET could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the GET request
     *
     * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes a <code>POST</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the POST could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the POST request
     *
     * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes all <code>GET</code> and <code>POST</code> requests and carries out
     * operations based on requested actions.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request could not be handled
     * @throws IOException if an input or output error is detected when the service
     *         handles the request
     */
    private void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        //retrieves the network management request
        NetworkManagementRequest networkRequest =
                (NetworkManagementRequest)request.getAttribute(ParameterNames.REQUEST_MANAGE_NETWORK);

        //checks that the request is available and is valid
        if(networkRequest != null && networkRequest.isValid())
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                       "Request: " + networkRequest.toString());

            //attempts to authorize the request
            boolean isActionAuthorized = false;

            //if a host interface or an internal network is to be created, no resource id can be provided
            if(networkRequest.getRequestedAction() == PermissionsConstants.ACTION_NETWORK_CREATE_HOST_IF
               || networkRequest.getRequestedAction() == PermissionsConstants.ACTION_NETWORK_MODIFY_HOST_IF
               || networkRequest.getRequestedAction() == PermissionsConstants.ACTION_NETWORK_REMOVE_HOST_IF)
            {
                if(sessionsManager.getUserData(networkRequest.getRequestorId()).getAccountLevel()
                        >= Constants.DB_USER_ACCOUNT_LEVEL_OWNER)
                    isActionAuthorized = true;
                else
                    ;
            }
            else if(networkRequest.getRequestedAction() == PermissionsConstants.ACTION_NETWORK_CREATE_INTERNAL_NET)
            {
                    isActionAuthorized = permissionsManager.isActionAuthorized(
                                            new SimpleAuthorizationRequest(networkRequest.getRequestorId(),
                                                                           networkRequest.getServerId(),
                                                                           networkRequest.getRequestedAction(),
                                                                           VirtualResourceType.NETWORK));
            }
            else //...regular authorization request is sent
            {
                    isActionAuthorized = permissionsManager.isActionAuthorized(
                                                new AuthorizationRequest(networkRequest.getRequestorId(),
                                                                         networkRequest.getServerId(),
                                                                         networkRequest.getRequestedAction(),
                                                                         VirtualResourceType.NETWORK,
                                                                         networkRequest.getNetworkId()));
            }

            //<editor-fold defaultstate="collapsed" desc="Action Selection">
            //checks if the request is authorized...
            if(isActionAuthorized)
            {
                //carries out the appropriate action based on the request data
                switch(networkRequest.getRequestedAction())
                {
                    //a new host interface is to be created
                    case PermissionsConstants.ACTION_NETWORK_CREATE_HOST_IF:
                    {
                        processResponse(request, createHostOnlyInterface((CreateHostOnlyInterfaceRequest)networkRequest));
                    } break;

                    //an existing host interface is to be removed
                    case PermissionsConstants.ACTION_NETWORK_REMOVE_HOST_IF:
                    {
                        processResponse(request, removeHostOnlyInterface((RemoveHostOnlyInterfaceRequest)networkRequest));
                    } break;

                    //an existing host interface is to be modified
                    case PermissionsConstants.ACTION_NETWORK_MODIFY_HOST_IF:
                    {
                        processResponse(request, modifyHostInterface((ModifyHostInterfaceRequest)networkRequest));
                    } break;

                    //a new internal network is to be added
                    case PermissionsConstants.ACTION_NETWORK_CREATE_INTERNAL_NET:
                    {
                        processResponse(request, createInternalNetwork((CreateInternalNetworkRequest)networkRequest));
                    } break;

                    //an existing internal network is to be removed
                    case PermissionsConstants.ACTION_NETWORK_REMOVE_INTERNAL_NET:
                    {
                        processResponse(request, removeInternalNetwork((RemoveInternalNetworkRequest)networkRequest));
                    } break;

                    //an existing internal network is to be modified
                    case PermissionsConstants.ACTION_NETWORK_MODIFY_INTERNAL_NET:
                    {
                        processResponse(request, modifyInternalNetwork((ModifyInternalNetworkRequest)networkRequest));
                    } break;

                    //a new DHCP server is to be added
                    case PermissionsConstants.ACTION_NETWORK_CREATE_DHCP:
                    {
                        processResponse(request, createDHCPServer((CreateDHCPServerRequest)networkRequest));
                    } break;

                    //an existing DHCP server is to be removed
                    case PermissionsConstants.ACTION_NETWORK_REMOVE_DHCP:
                    {
                        processResponse(request, removeDHCPServer((RemoveDHCPServerRequest)networkRequest));
                    } break;

                    //an existing DHCP server is to be modified
                    case PermissionsConstants.ACTION_NETWORK_MODIFY_DHCP:
                    {
                        processResponse(request, modifyDHCPServer((ModifyDHCPServerRequest)networkRequest));
                    } break;

                    default:
                    {
                        processResponse(request, new NetworkManagementResponse(false, 
                                new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUESTED_ACTION)));
                    } break;
                }
            }
            else
            {
                processResponse(request, new NetworkManagementResponse(false, 
                        new ApplicationException(ExceptionMessages.APP_E_OPERATION_NOT_ALLOWED)));
            }
            //</editor-fold>
        }
        else
        {
            processResponse(request, new NetworkManagementResponse(false, 
                    new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUEST)));
        }
    }

    /**
     * Processes all responses going out of the service.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @param networkResponse network management response object
     * @throws ServletException if the processing of the response could not be handled
     * @throws IOException if an input or output error is detected when the response
     *                     is handled
     */
    private void processResponse(HttpServletRequest request, NetworkManagementResponse networkResponse)
            throws ServletException, IOException
    {
        //retrieves the request object from the session
        RequestBean requestData = (RequestBean)request.getAttribute(ParameterNames.REQUEST_MANAGE_NETWORK);
        request.setAttribute(ParameterNames.REQUEST_MANAGE_NETWORK, null);
        request.setAttribute(ParameterNames.RESPONSE_MANAGE_NETWORK, networkResponse);

        //checks if the requested action failed
        if(!networkResponse.getRequestResult()
                || (networkResponse.getExceptions() != null && !networkResponse.getExceptions().isEmpty()))
        {
            //the request failed; retrieves the message container and stores the request/response pair
            ResponseMessagesContainer messagesContainer = (ResponseMessagesContainer)
                    request.getSession(false).getAttribute(ParameterNames.SESSION_REQUEST_MESSAGES_OBJECT);

            messagesContainer.put(new RequestResponseWrapper(requestData, networkResponse));

            if(networkResponse.getExceptions() == null)
            {
                logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                           "Request execution failed: "
                                           + networkResponse.getException().toString());
            }
            else
            {
                for(ApplicationException exception : networkResponse.getExceptions())
                {
                    logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                       "Request execution failed: " + exception.toString());
                }
            }
        }
        else
            ;
    }

    /**
     * Creates a new host-only interface.
     *
     * @param request host-only interface creation request
     * @return a response to the network management request
     */
    private NetworkManagementResponse createHostOnlyInterface(CreateHostOnlyInterfaceRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        IHost host = null;

        //checks if the virtualbox object was retrieved
        if(vbox != null)
        {
            //attempts to retrieve the IHost object
            host = vbox.getHost();
            if(host != null)
                passedPreOp = true;
            else
                preOpException = new ApplicationException(ExceptionMessages.APP_E_HOST_RETRIEVAL_FAILED);
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;
        String newInterfaceId = null;
        IProgress operationProgress = null;

        try
        {
            //attempts to create a new host-only interface
            Holder<IHostNetworkInterface> newInterfaceHolder = new Holder<IHostNetworkInterface>();
            operationProgress = host.createHostOnlyNetworkInterface(newInterfaceHolder);

            //TODO: maybe create a separate thread that will wait for the completion and register the
            //interface
            //waits for the creation operation to complete or the maximum wait time
            operationProgress.waitForCompletion(maxOperationWaitTime);
            newInterfaceId = newInterfaceHolder.value.getId(); //retrieves the new interface's id

            //checks the new interface's id
            if(newInterfaceId != null)
                passedOp = true;
            else
                opException = new ApplicationException(ExceptionMessages.APP_E_HOST_ONLY_UUID_RETRIEVAL_FAILED);
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }

        if(!passedOp)
            return new NetworkManagementResponse(false, opException, operationProgress);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //attempts to add the new interface to the database
        DatabaseManipulationResult result =
                databaseManager.insertVirtualResource(new VirtualResourceBean(newInterfaceId,
                                                                              request.getServerId(),
                                                                              request.getRequestorId(),
                                                                              request.getPermissions(),
                                                                              VirtualResourceType.NETWORK));

        if(result.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
            postOpException = new ApplicationException(ExceptionMessages.APP_E_NETWORK_EXISTS_IN_DB);
        else
        {
            postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                       "(" + result.getErrorCode() + ")");
        }

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException, operationProgress);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, operationProgress);
    }

    /**
     * Removes an existing host interface.
     *
     * @param request host interface removal request
     * @return a response to the network management request
     */
    private NetworkManagementResponse removeHostOnlyInterface(RemoveHostOnlyInterfaceRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        IHost host = null;
        if(vbox != null)
        {
            host = vbox.getHost(); //retrieves the IHost object
            if(host != null)
            {
                try
                {
                    //attempts to find the host interface by ID
                    IHostNetworkInterface hostInterface = host.findHostNetworkInterfaceById(request.getNetworkId());

                    try
                    {
                        //attempts to find and remove the interface's DHCP server
                        IDHCPServer dhcp = vbox.findDHCPServerByNetworkName(hostInterface.getNetworkName());
                        vbox.removeDHCPServer(dhcp);
                    }
                    catch(WebServiceException e){}

                    passedPreOp = true;
                }
                catch(WebServiceException e)
                {
                    preOpException = Functions.parseVirtualBoxException(e);
                }
            }
            else
                preOpException = new ApplicationException(ExceptionMessages.APP_E_HOST_RETRIEVAL_FAILED);
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;
        IProgress operationProgress = null;

        try
        {
            //attempts to remove the host-only interface
            operationProgress = host.removeHostOnlyNetworkInterface(request.getNetworkId());
            //waits for the operation to complete or for the maximum wait time
            operationProgress.waitForCompletion(maxOperationWaitTime);
            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }

        if(!passedOp)
            return new NetworkManagementResponse(false, opException, operationProgress);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //attempts to remove the interface from the database
        DatabaseManipulationResult result =
                databaseManager.removeVirtualResource(request.getNetworkId(), request.getServerId());

        if(result.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_REMOVED)
            postOpException = new ApplicationException(ExceptionMessages.APP_E_HOST_ONLY_IF_REMOVAL_FAILED);
        else
            postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                       "(" + result.getErrorCode() + ")");

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException, operationProgress);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, operationProgress);
    }

    /**
     * Modifies an existing host interface.
     *
     * @param request host interface modification request
     * @return a response to the network management request
     */
    private NetworkManagementResponse modifyHostInterface(ModifyHostInterfaceRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        IHostNetworkInterface hostInterface = null;
        if(vbox != null)
        {
            try
            {
                //attemtps to find the host interface by id
                hostInterface = vbox.getHost().findHostNetworkInterfaceById(request.getNetworkId());
                if(hostInterface != null)
                    passedPreOp = true;
                else
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_HOST_RETRIEVAL_FAILED);
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = true;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();

        try
        {
            //checks if the interface's IP configuration is to be set
            if(request.isIPConfigurationSet())
            {                
                if(request.isDynamicIPConfigurationEnabled())
                    hostInterface.enableDynamicIpConfig();//set the dynamic IP config
                else
                {
                    if(request.isIPv6StaticConfigurationEnabled())//set the statis IPv6 config
                        hostInterface.enableStaticIpConfigV6(request.getStaticIPAddress(),
                                                             request.getStaticIPv6MaskPrefixLength());
                    else //sets the stati IPv4 config
                        hostInterface.enableStaticIpConfig(request.getStaticIPAddress(),
                                                           request.getStaticIPv4Mask());
                }
            }
            else
                ;

            //checks if DHCP rediscover is to be executed
            if(request.getDHCPRediscoverState())
                hostInterface.dhcpRediscover();
            else
                ;

            //checks if new permissions are to be set
            if(request.arePermissionsSet())
            {
                //attempts to update the interface's permissions
                DatabaseManipulationResult result =
                        databaseManager.updateVirtualResourcePermissions(request.getNetworkId(),
                                                                         request.getServerId(),
                                                                         request.getPermissions());

                if(result.wasOperationSuccessful()) //was the operation successful?
                    ;
                else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                {
                    opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_NETWORK_PERMISSIONS_UPDATE_FAILED));
                    passedOp = false;
                }
                else
                {
                    opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                              "(" + result.getErrorCode() + ")"));
                    passedOp = false;
                }
            }
            else
                ;

            //checks if a new owner is to be set
            if(request.isNewOwnerIdSet())
            {
                //attempts to set the new owner
                DatabaseManipulationResult result =
                        databaseManager.changeVirtualResourceOwner(request.getNetworkId(),
                                                                   request.getServerId(),
                                                                   request.getNewOwnerId());

                if(result.wasOperationSuccessful()) //was the operation successful?
                    ;
                else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                {
                    opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_NETWORK_OWNER_UPDATE_FAILED));
                    passedOp = false;
                }
                else
                {
                    opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                              "(" + result.getErrorCode() + ")"));
                    passedOp = false;
                }
            }
            else
                ;
        }
        catch(WebServiceException e)
        {
            passedOp = false;
            opExceptions.add(Functions.parseVirtualBoxException(e));
        }

        if(!passedOp)
            return new NetworkManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, opExceptions);
    }

    /**
     * Creates a new internal network.
     *
     * @param request internal network creation request
     * @return a response to the network management request
     */
    private NetworkManagementResponse createInternalNetwork(CreateInternalNetworkRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;

        UUID newNetworkId = UUID.randomUUID(); //generates new UUID for the internal network

        //attempts to add the internal network in the database
        DatabaseManipulationResult result =
                databaseManager.insertInternalNetwork(new InternalNetworkBean(newNetworkId.toString(),
                                                                              request.getServerId(),
                                                                              request.getExternalName()));

        if(result.wasOperationSuccessful()) //was the operation successful?
            passedOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
            opException = new ApplicationException(ExceptionMessages.APP_E_NETWORK_EXISTS_IN_DB);
        else
            opException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                   "(" + result.getErrorCode() + "");

        if(!passedOp)
            return new NetworkManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //attemtps to add the internal network to the virtual resources
        DatabaseManipulationResult postOpResult =
                databaseManager.insertVirtualResource(new VirtualResourceBean(newNetworkId.toString(),
                                                                              request.getServerId(),
                                                                              request.getRequestorId(),
                                                                              request.getPermissions(),
                                                                              VirtualResourceType.NETWORK));

        if(postOpResult.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(postOpResult.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
            postOpException = new ApplicationException(ExceptionMessages.APP_E_NETWORK_EXISTS_IN_DB);
        else
            postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                       "(" + postOpResult.getErrorCode() + ")");

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Removes an existing internal network.
     *
     * @param request internal network removal request
     * @return a response to the network management request
     */
    private NetworkManagementResponse removeInternalNetwork(RemoveInternalNetworkRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the internal network's data
        InternalNetworkBean networkData = databaseManager.getInternalNetwork(request.getNetworkId(), request.getServerId());

        if(networkData != null)
        {
            //checks if there are machines with adapters attached to the network
            if(networkData.getNumberOfAttachedNICs() == 0)
            {
                //checks if there is a DHCP associated with the network
                if(networkData.isDHCPAvailable())
                {
                    //builds the DHCP server removal request
                    RemoveDHCPServerRequest dhcpRemoveRequest =
                            new RemoveDHCPServerRequest(request.getRequestorId(), request.getServerId(),
                                                        request.getNetworkId(), NetworkType.INTERNAL);

                    //attempts to remove the DHCP server
                    NetworkManagementResponse dhcpRemoveResponse = removeDHCPServer(dhcpRemoveRequest);

                    if(dhcpRemoveResponse.getRequestResult())
                        passedPreOp = true;
                    else
                        preOpException = dhcpRemoveResponse.getException();
                }
                else
                    passedPreOp = true;
            }
            else if(networkData.getNumberOfAttachedNICs() == Utilities.Constants.INVALID_NIC_NUMBER)
                preOpException = new ApplicationException(ExceptionMessages.APP_E_NETWORK_ATTACHED_NICS_RETRIEVAL_FAILED);
            else
                preOpException = new ApplicationException(ExceptionMessages.APP_E_NETWORK_MACHINES_ARE_ATTACHED);
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_NETWORKS_RETRIEVAL_FAILED);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;

        //attempts to remove the internal network's main data from the database
        DatabaseManipulationResult result =
                databaseManager.removeInternalNetwork(request.getNetworkId(), request.getServerId());

        if(result.wasOperationSuccessful()) //was the operation successful?
            passedOp = true;
        else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_REMOVED)
            opException = new ApplicationException(ExceptionMessages.APP_E_INTERNAL_NETWORK_REMOVAL_FAILED);
        else
            opException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                   "(" + result.getErrorCode() + ")");

        if(!passedOp)
            return new NetworkManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //attempts to remove the remaining internal network data from the database
        DatabaseManipulationResult postOpResult =
                databaseManager.removeVirtualResource(request.getNetworkId(), request.getServerId());

        if(postOpResult.wasOperationSuccessful()) //was the operation successful?
                passedPostOp = true;
        else if(postOpResult.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
            postOpException = new ApplicationException(ExceptionMessages.APP_E_INTERNAL_NETWORK_REMOVAL_FAILED);
        else
            postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                       "(" + postOpResult.getErrorCode() + ")");

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Modifies an existing internal network.
     *
     * @param request internal network modification request
     * @return a response to the network management request
     */
    private NetworkManagementResponse modifyInternalNetwork(ModifyInternalNetworkRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = true;
        ArrayList<ApplicationException> opExceptions = new ArrayList<ApplicationException>();

        //checks if new permissions are to be set
        if(request.arePermissionsSet())
        {
            //attempts to update the permissions
            DatabaseManipulationResult result =
                    databaseManager.updateVirtualResourcePermissions(request.getNetworkId(),
                                                                     request.getServerId(),
                                                                     request.getPermissions());

            if(result.wasOperationSuccessful()) //was the operation successful?
                ;
            else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
            {
                opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_NETWORK_PERMISSIONS_UPDATE_FAILED));
                passedOp = false;
            }
            else
            {
                opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                          "(" + result.getErrorCode() + ")"));
                passedOp = false;
            }
        }
        else
            ;

        //checks if a new external name is to be set
        if(request.isNewExternalNameSet())
        {
            //attempts to set the new external name
            DatabaseManipulationResult result =
                    databaseManager.changeInternalNetworkName(request.getNetworkId(),
                                                              request.getServerId(),
                                                              request.getNewExternalName());

            if(result.wasOperationSuccessful()) //was the operation successful?
                ;
            else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
            {
                opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_NETWORK_EXTERNAL_NAME_UPDATE_FAILED));
                passedOp = false;
            }
            else
            {
                opExceptions.add(new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                          "(" + result.getErrorCode() + ")"));
                passedOp = false;
            }
        }
        else
            ;

        if(!passedOp)
            return new NetworkManagementResponse(false, opExceptions);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Creates a new DHCP server.
     *
     * @param request DHCP server creation request
     * @return a response to the network management request
     */
    private NetworkManagementResponse createDHCPServer(CreateDHCPServerRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        if(vbox != null)
            passedPreOp = true;
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;

        try
        {
            //attemtps to retrieve the proper network name/id for the new DHCP server
            String dhcpServerNetworkName = null;
            switch(request.getNetworkType())
            {
                case INTERNAL:
                    dhcpServerNetworkName = request.getNetworkId(); break;

                case HOST_ONLY:
                {
                    try
                    {
                        //retrieves the host interface and the proper network name
                        IHostNetworkInterface hostInterface =
                                vbox.getHost().findHostNetworkInterfaceById(request.getNetworkId());
                        dhcpServerNetworkName = hostInterface.getNetworkName();
                    }
                    catch(WebServiceException e){}
                } break;
            }

            //checks if the network name was retrieved
            if(dhcpServerNetworkName != null)
            {
                //creates the new DHCP server
                IDHCPServer dhcpServer = vbox.createDHCPServer(dhcpServerNetworkName);

                //sets the DHCP server's configuration, if available
                DHCPServerConfiguration serverConfiguration = request.getServerConfiguration();
                if(serverConfiguration != null)
                {
                    dhcpServer.setConfiguration(serverConfiguration.getServerIPAddress(),
                                                serverConfiguration.getServerNetworkMask(),
                                                serverConfiguration.getAddressRangeLow(),
                                                serverConfiguration.getAddressRangeHigh());

                    dhcpServer.setEnabled(request.getDHCPServerState());
                }
                else
                    ;

                passedOp = true;
            }
            else
                opException = new ApplicationException(ExceptionMessages.APP_E_DHCP_NETWORK_NAME_RETRIEVAL_FAILED);
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }

        if(!passedOp)
            return new NetworkManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //checks if an internal network needs to be updated that it got a DHCP server
        if(request.getNetworkType() == NetworkType.INTERNAL)
        {
            //attempts to make the update
            DatabaseManipulationResult postOpResult =
                    databaseManager.changeInternalNetworkDHCPAvailability(request.getNetworkId(),
                                                                          request.getServerId(),
                                                                          true);

            if(postOpResult.wasOperationSuccessful()) //was the operation successful?
                    passedPostOp = true;
            else if(postOpResult.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                postOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_AVAILABILITY_UPDATE_FAILED);
            else
                postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                           "(" + postOpResult.getErrorCode() + ")");
        }
        else
            passedPostOp = true;

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Removes an existing DHCP server.
     *
     * @param request DHCP server removal request
     * @return a response to the network management request
     */
    private NetworkManagementResponse removeDHCPServer(RemoveDHCPServerRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        IDHCPServer dhcpServer = null;
        if(vbox != null)
        {
            try
            {
                //attemtps to retrieve the proper network name/id for the DHCP server
                String dhcpServerNetworkName = null;
                switch(request.getNetworkType())
                {
                    case INTERNAL:
                        dhcpServerNetworkName = request.getNetworkId(); break;

                    case HOST_ONLY:
                    {
                        try
                        {
                            //retrieves the host interface and the proper network name
                            IHostNetworkInterface hostInterface =
                                    vbox.getHost().findHostNetworkInterfaceById(request.getNetworkId());
                            dhcpServerNetworkName = hostInterface.getNetworkName();
                        }
                        catch(WebServiceException e){}
                    } break;
                }

                //checks if the network name was retrieved
                if(dhcpServerNetworkName != null)
                {
                    //attempts to retrieve the DHCP server
                    dhcpServer = vbox.findDHCPServerByNetworkName(dhcpServerNetworkName);
                    if(dhcpServer != null)
                        passedPreOp = true;
                    else
                        preOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_OBJECT_RETRIEVAL_FAILED);
                }
                else
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_NETWORK_NAME_RETRIEVAL_FAILED);
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################
        boolean passedOp = false;
        ApplicationException opException = null;

        try
        {
            //attempts to remove the DHCP server
            vbox.removeDHCPServer(dhcpServer);
            passedOp = true;
        }
        catch(WebServiceException e)
        {
            opException = Functions.parseVirtualBoxException(e);
        }

        if(!passedOp)
            return new NetworkManagementResponse(false, opException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="PostOp">
        //PostOp ##########################################################################
        boolean passedPostOp = false;
        ApplicationException postOpException = null;

        //checks if an internal network needs to be updated that it doesn't have a DHCP server
        if(request.getNetworkType() == NetworkType.INTERNAL)
        {
            //attempts to make the update
            DatabaseManipulationResult postOpResult =
                    databaseManager.changeInternalNetworkDHCPAvailability(request.getNetworkId(),
                                                                          request.getServerId(),
                                                                          false);

            if(postOpResult.wasOperationSuccessful()) //was the operation successful?
                    passedPostOp = true;
            else if(postOpResult.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
                postOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_AVAILABILITY_UPDATE_FAILED);
            else
                postOpException = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                           "(" + postOpResult.getErrorCode() + ")");
        }
        else
            passedPostOp = true;

        if(!passedPostOp)
            return new NetworkManagementResponse(false, postOpException);
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }

    /**
     * Modifies an existing DHCP server.
     *
     * @param request DHCP server modification request
     * @return a response to the network management request
     */
    private NetworkManagementResponse modifyDHCPServer(ModifyDHCPServerRequest request)
    {
        //<editor-fold defaultstate="collapsed" desc="PreOp">
        //PreOp ##########################################################################
        boolean passedPreOp = false;
        ApplicationException preOpException = null;

        //attemts to retrieve the virtualbox object for the required server
        IVirtualBox vbox = connectionsManager.getVirtualBoxReference(request.getServerId(), request.getRequestorId());
        IDHCPServer dhcpServer = null;
        if(vbox != null)
        {
            try
            {
                //attemtps to retrieve the proper network name/id for the DHCP server
                String dhcpServerNetworkName = null;
                switch(request.getNetworkType())
                {
                    case INTERNAL:
                        dhcpServerNetworkName = request.getNetworkId(); break;

                    case HOST_ONLY:
                    {
                        try
                        {
                            //retrieves the host interface and the proper network name
                            IHostNetworkInterface hostInterface =
                                    vbox.getHost().findHostNetworkInterfaceById(request.getNetworkId());
                            dhcpServerNetworkName = hostInterface.getNetworkName();
                        }
                        catch(WebServiceException e){}
                    } break;
                }

                //checks if the network name was retrieved
                if(dhcpServerNetworkName != null)
                {
                    //attempts to retrieve the DHCP server
                    dhcpServer = vbox.findDHCPServerByNetworkName(dhcpServerNetworkName);
                    if(dhcpServer != null)
                        passedPreOp = true;
                    else
                        preOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_OBJECT_RETRIEVAL_FAILED);
                }
                else
                    preOpException = new ApplicationException(ExceptionMessages.APP_E_DHCP_NETWORK_NAME_RETRIEVAL_FAILED);
            }
            catch(WebServiceException e)
            {
                preOpException = Functions.parseVirtualBoxException(e);
            }
        }
        else
            preOpException = new ApplicationException(ExceptionMessages.APP_E_VBOX_REF_NOT_AVAILABLE);

        if(!passedPreOp)
            return new NetworkManagementResponse(false, preOpException);
        else
            ;
        //</editor-fold>

        //<editor-fold defaultstate="collapsed" desc="Op">
        //Op ##########################################################################

        //checks if the DHCP server configuration is to be modified
        DHCPServerConfiguration serverConfiguration = request.getServerConfiguration();
        if(serverConfiguration != null)
        {
            dhcpServer.setConfiguration(serverConfiguration.getServerIPAddress(),
                                        serverConfiguration.getServerNetworkMask(),
                                        serverConfiguration.getAddressRangeLow(),
                                        serverConfiguration.getAddressRangeHigh());
        }
        else
            ;

        //checks if the DHCP server state is to be modified
        if(request.isDHCPStateSet())
            dhcpServer.setEnabled(request.getDHCPState());
        else
            ;
        //</editor-fold>

        //Result ##########################################################################
        return new NetworkManagementResponse(true, (ApplicationException)null);
    }
}
